


#include "napi/native_api.h"
#include "hilog/log.h"
#include <cstdio>
#include <future>
#include "dlfcn.h"
extern "C"{
    #include "TestSub/TestSub.h"
}


#undef LOG_DOMAIN
#undef LOG_TAG
#define LOG_DOMAIN 0x3200  // 全局domain宏，标识业务领域
#define LOG_TAG "MY_TAG"   // 全局tag宏，标识模块日志tag

static napi_value Add(napi_env env, napi_callback_info info) {
    size_t argc = 2;
    napi_value args[2] = {nullptr};

    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

    napi_valuetype valuetype0;
    napi_typeof(env, args[0], &valuetype0);

    napi_valuetype valuetype1;
    napi_typeof(env, args[1], &valuetype1);

    double value0;
    napi_get_value_double(env, args[0], &value0);

    double value1;
    napi_get_value_double(env, args[1], &value1);

    napi_value sum;
    napi_create_double(env, value0 + value1, &sum);
    
    return sum;
}

static napi_value Sub(napi_env env, napi_callback_info info) {
    size_t argc = 2;
    napi_value args[2] = {nullptr};

    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

    napi_valuetype valuetype0;
    napi_typeof(env, args[0], &valuetype0);

    napi_valuetype valuetype1;
    napi_typeof(env, args[1], &valuetype1);

    double value0;
    napi_get_value_double(env, args[0], &value0);

    double value1;
    napi_get_value_double(env, args[1], &value1);

    napi_value res;
    napi_create_double(env, sub(value0, value1), &res);
    
    return res;
}

typedef double(*SubTest)(double, double);
static napi_value addSoByDlopen(napi_env env, napi_callback_info info) {
    // TODO: implements the code;
    size_t argc = 0;

    napi_get_cb_info(env, info, &argc, nullptr, nullptr, nullptr);
    
    if (argc==0) {
        return nullptr;
    }
    napi_value* args = new napi_value[argc];
    
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    
    size_t strSize;
    char path[256];
    napi_get_value_string_utf8(env, args[0], path,sizeof(path),&strSize);

    double value1;
    napi_get_value_double(env, args[1], &value1);
    
    double value2;
    napi_get_value_double(env, args[2], &value2);
    
    
    // Print the values
    OH_LOG_ERROR(LOG_APP, "------", "", LOG_TAG);

    void *testSub = dlopen(path,RTLD_LAZY);
    
    SubTest subtest = (SubTest)dlsym(testSub, "sub");

    double subRes = subtest(value1,value2);
    dlclose(testSub);
    
    napi_value res;
    napi_create_double(env, subRes, &res);
    
    return res;
    
}

// 同步callback
static napi_value nativeCallArkTS(napi_env env, napi_callback_info info) {
    size_t argc = 0;
    napi_get_cb_info(env, info, &argc, nullptr, nullptr, nullptr);
    if (argc==0) {
        return nullptr;
    }
    napi_value* args = new napi_value[argc];
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

    double value_a;
    napi_get_value_double(env, args[1], &value_a);

    double value_b;
    napi_get_value_double(env, args[2], &value_b);

    // 创建一个int，作为ArkTS的入参
    napi_value value_c = nullptr;    
    napi_create_int32(env, value_a+value_b, &value_c );
    
    napi_value result = nullptr;
    napi_call_function(env, nullptr, args[0], 1, &value_c, &result);
    
    return result;
}

//异步任务
struct CallbackData {
    napi_async_work asyncWork = nullptr;
    napi_deferred deferred = nullptr;
    napi_ref callback = nullptr;
    double args[2] = {0};
    double result = 0;
};
static void ExecuteCB(napi_env env, void *data)
{
    CallbackData *callbackData = reinterpret_cast<CallbackData *>(data);
    callbackData->result = callbackData->args[0] + callbackData->args[1];
}
static void CompleteCB(napi_env env, napi_status status, void *data)
{
    CallbackData *callbackData = reinterpret_cast<CallbackData *>(data);
    napi_value result = nullptr;
    napi_create_double(env, callbackData->result, &result);
    if (callbackData->result > 0) {
        napi_resolve_deferred(env, callbackData->deferred, result);
    } else {
        napi_reject_deferred(env, callbackData->deferred, result);
    }

    napi_delete_async_work(env, callbackData->asyncWork);
    delete callbackData;
}
static napi_value asyncNativeCall(napi_env env, napi_callback_info info){
    size_t argc = 0;
    napi_get_cb_info(env, info, &argc, nullptr, nullptr, nullptr);
    if (argc==0) {
        return nullptr;
    }
    napi_value* args = new napi_value[argc];
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);


    napi_value promise = nullptr;
    napi_deferred deferred = nullptr;
    napi_create_promise(env, &deferred, &promise);
    
    auto callbackData = new CallbackData();
    callbackData->deferred = deferred;
    napi_get_value_double(env, args[0], &callbackData->args[0]);
    napi_get_value_double(env, args[1], &callbackData->args[1]);
    
    napi_value resourceName = nullptr;
    napi_create_string_utf8(env, "AsyncTestAdd", NAPI_AUTO_LENGTH, &resourceName);
    //创建异步任务
    napi_create_async_work(env, nullptr, resourceName, ExecuteCB, CompleteCB, callbackData, &callbackData->asyncWork);
    //将异步任务加入队列
    napi_queue_async_work(env, callbackData->asyncWork);
    
    return promise;
}


//线程安全示例
struct ThreadSafeCallbackData {
    napi_threadsafe_function tsfn;
    napi_async_work work;
};

static void ExecuteWork(napi_env env, void *data)
{
    ThreadSafeCallbackData *callbackData = reinterpret_cast<ThreadSafeCallbackData *>(data);
    std::promise<std::string> promise;
    auto future = promise.get_future();
    napi_call_threadsafe_function(callbackData->tsfn, &promise, napi_tsfn_nonblocking);
    try {
        auto result = future.get();
        OH_LOG_INFO(LOG_APP, "XXX, Result from JS %{public}s", result.c_str());
    } catch (const std::exception &e) {
        OH_LOG_INFO(LOG_APP, "XXX, Result from JS %{public}s", e.what());
    }
}

static napi_value ResolvedCallback(napi_env env, napi_callback_info info)
{
    void *data = nullptr;
    size_t argc = 1;
    napi_value argv[1];
    if (napi_get_cb_info(env, info, &argc, argv, nullptr, &data) != napi_ok) {
        return nullptr;
    }
    size_t result = 0;
    char buf[32] = {0};
    napi_get_value_string_utf8(env, argv[0], buf, 32, &result);
    reinterpret_cast<std::promise<std::string> *>(data)->set_value(std::string(buf));
    return nullptr;
}

static napi_value RejectedCallback(napi_env env, napi_callback_info info)
{
    void *data = nullptr;
    if (napi_get_cb_info(env, info, nullptr, nullptr, nullptr, &data) != napi_ok) {
        return nullptr;
    }
    reinterpret_cast<std::promise<std::string> *>(data)->set_exception(
        std::make_exception_ptr(std::runtime_error("Error in jsCallback")));
    return nullptr;
}



static void WorkComplete(napi_env env, napi_status status, void *data)
{
    ThreadSafeCallbackData *callbackData = reinterpret_cast<ThreadSafeCallbackData *>(data);
    napi_release_threadsafe_function(callbackData->tsfn, napi_tsfn_release);
    napi_delete_async_work(env, callbackData->work);
    callbackData->tsfn = nullptr;
    callbackData->work = nullptr;
}

static void CallJs(napi_env env, napi_value jsCb, void *context, void *data)
{
    if (env == nullptr) {
        return;    
    }
    napi_value undefined = nullptr;
    napi_value promise = nullptr;
    napi_get_undefined(env, &undefined);
    napi_call_function(env, undefined, jsCb, 0, nullptr, &promise);
    napi_value thenFunc = nullptr;
    if (napi_get_named_property(env, promise, "then", &thenFunc) != napi_ok) {
        return;
    }
    napi_value resolvedCallback;
    napi_value rejectedCallback;
    napi_create_function(env, "resolvedCallback", NAPI_AUTO_LENGTH, ResolvedCallback, data,
                         &resolvedCallback);
    napi_create_function(env, "rejectedCallback", NAPI_AUTO_LENGTH, RejectedCallback, data,
                         &rejectedCallback);
    napi_value argv[2] = {resolvedCallback, rejectedCallback};
    napi_call_function(env, promise, thenFunc, 2, argv, nullptr);
}

static napi_value startThread(napi_env env, napi_callback_info info)
{
    size_t argc = 0;
    napi_get_cb_info(env, info, &argc, nullptr, nullptr, nullptr);
    if (argc==0) {
        return nullptr;
    }
    napi_value jsCb = nullptr;
    ThreadSafeCallbackData *callbackData = nullptr;
    napi_get_cb_info(env, info, &argc, &jsCb, nullptr, reinterpret_cast<void **>(&callbackData));

    // 创建一个线程安全函数
    napi_value resourceName = nullptr;
    napi_create_string_utf8(env, "Thread-safe Function Demo", NAPI_AUTO_LENGTH, &resourceName);
    napi_create_threadsafe_function(env, jsCb, nullptr, resourceName, 0, 1, callbackData, nullptr, 
        callbackData, CallJs, &callbackData->tsfn);

    // 创建一个异步任务
    napi_create_async_work(env, nullptr, resourceName, ExecuteWork, WorkComplete, callbackData,
        &callbackData->work);

    // 将异步任务加入到异步队列中
    napi_queue_async_work(env, callbackData->work);
    return nullptr;
}





EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports) {
    
    ThreadSafeCallbackData *callbackData = new ThreadSafeCallbackData(); // 可在线程退出时释放

    napi_property_descriptor desc[] = {
        {"add", nullptr, Add, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"sub", nullptr, Sub, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"addSoByDlopen", nullptr, addSoByDlopen, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"nativeCallArkTS", nullptr, nativeCallArkTS, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"asyncNativeCall", nullptr, asyncNativeCall, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"startThread", nullptr, startThread, nullptr, nullptr, nullptr, napi_default, callbackData}
    };

    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
    return exports;
}
EXTERN_C_END

static napi_module demoModule = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = nullptr,
    .nm_register_func = Init,
    .nm_modname = "entry",
    .nm_priv = ((void *)0),
    .reserved = {0},
};

extern "C" __attribute__((constructor)) void RegisterEntryModule(void) { napi_module_register(&demoModule); }
